using System;
using System.Reflection;
using System.Runtime.Serialization;
using System.Collections.Generic;
using Crainiate.Data.Persistence.Providers;
using Crainiate.Data.Persistence.Cache;

namespace Crainiate.Data.Persistence
{
	//Strongly typed key value pair collection containing previous values
    //Dictionary implements ISerializable
    [Serializable]
	public class Concurrency: Dictionary<string, object>
	{
		//Property variables
		private bool _enabled;
        private ConcurrencyStatus _status;

		#region Interface

        public Concurrency()
        {
            _enabled = true;
            _status = ConcurrencyStatus.None;
        }

        protected Concurrency(SerializationInfo info, StreamingContext context): base(info, context)
        {
            _enabled = info.GetBoolean("Enabled");
            _status = (ConcurrencyStatus) Enum.Parse(typeof(ConcurrencyStatus), info.GetString("Status"));
        }

		public virtual bool Enabled
		{
			get
			{
				return _enabled;
			}
			set
			{
                if (value != _enabled)
                {
                    _enabled = value;
                }
			}
		}

        public virtual void Reset()
        {
            Clear();
            _status = ConcurrencyStatus.None;
        }

        public virtual ConcurrencyStatus Status
        {
            get
            {
                return _status;
            }
            set
            {
                _status = value;
            }
        }

        public virtual void FromObject(IPersistable graph)
        {
            if (Enabled)
            {
                Clear();
                ValuesFromObject(graph, graph.GetType());
            }
        }

        public virtual bool HasChanged(IPersistable graph)
        {
            return DetermineChanges(graph, graph.GetType());
        }

        #endregion

        #region Overrides

        public override void GetObjectData(SerializationInfo info, StreamingContext context)
        {
            base.GetObjectData(info, context);

            info.AddValue("Enabled", _enabled);
            info.AddValue("Status", Convert.ToInt32(_status).ToString());
        }

        #endregion

        #region Implementation

        private void ValuesFromObject(IPersistable graph, Type type)
        {
            ReflectionCacheItem cache = Component.Instance.ReflectionCache.Lookup(type);

            if (cache.UnderlyingType != type) ValuesFromObject(graph, cache.BaseType);

            foreach (PropertyInfo prop in cache.Properties)
            {
                string name = Crainiate.Data.Persistence.Providers.Formatter.GetPropertyName(prop, ".");
                Add(name, prop.GetValue(graph, null));
            }
        }

        private bool DetermineChanges(IPersistable graph, Type type)
        {
            ReflectionCacheItem cache = Component.Instance.ReflectionCache.Lookup(type);

            if (cache.UnderlyingType != type)
            {
                if (DetermineChanges(graph, cache.BaseType)) return true;
            }

            foreach (PropertyInfo prop in cache.Properties)
            {
                string name = Crainiate.Data.Persistence.Providers.Formatter.GetPropertyName(prop, ".");

                if (ContainsKey(name) && !prop.GetValue(graph, null).Equals(this[name])) return true;
            }

            return false;
        }

        #endregion
    }
}